export interface IOptions {
  editor: HTMLDivElement
  onChange: () => void
}

export default class MySelection {
  editor: HTMLDivElement
  range?: Range
  observer: MutationObserver
  onChange: () => void
  constructor(ops: IOptions) {
    this.editor = ops.editor

    this.editor.addEventListener('paste', e => {
      const clipboardData = e.clipboardData || window.clipboardData
      if (!clipboardData) return false
      const paste = clipboardData.getData('text')

      const selection = window.getSelection()
      if (!selection || !selection.rangeCount) return false
      const range = selection.getRangeAt(0)
      range.deleteContents()
      range.insertNode(document.createTextNode(paste))
      range.collapse(false)

      e.preventDefault()
    })
    this.editor.addEventListener('keydown', e => {
      if (e.key === 'Backspace') {
        const selObj = window.getSelection()
        if (
          !selObj ||
          !selObj.focusNode ||
          selObj.focusNode.nodeType !== 1 ||
          selObj.focusOffset === 0
        )
          return
        const node = selObj.focusNode as HTMLElement
        const index = selObj.focusOffset - 1
        if (node === this.editor && node.childNodes[index].nodeType === 1) {
          const target = node.childNodes[index] as HTMLElement
          if (target.getAttribute('contenteditable') === 'false') {
            this.editor.removeChild(target)
            return (e.returnValue = false)
          }
        }
      } else if (e.key === 'Enter') {
        return (e.returnValue = false)
      }
    })
    this.editor.addEventListener('keyup', this.keyUp.bind(this))
    this.onChange = ops.onChange
    const config = { childList: true, subtree: true, characterData: true }
    this.observer = new MutationObserver(this.onChange)
    this.observer.observe(this.editor, config)
  }

  resetOnChange(fn: () => void) {
    this.destroy()
    const config = { childList: true, subtree: true, characterData: true }
    this.observer = new MutationObserver(fn)
    this.observer.observe(this.editor, config)
  }

  destroy() {
    if (this.observer && this.observer.disconnect) {
      this.observer.disconnect()
    }
  }

  keyUp(e: KeyboardEvent) {
    if (e.key !== 'Enter') return
    const selObj = window.getSelection()
    if (!selObj) return

    const range = selObj.getRangeAt(0)
    const br = document.createElement('br')
    range.deleteContents()
    range.insertNode(br)
    this.editor.normalize()
    if (!br.nextSibling) {
      const br = document.createElement('br')
      this.editor.appendChild(br)
    }
    this.setCursorPosition(br)
  }

  recordSelection() {
    const selObj = window.getSelection()
    if (!selObj) return

    this.range = selObj.getRangeAt(0)
  }

  insert(el: Node) {
    if (!this.range) return

    this.range.deleteContents()
    this.range.insertNode(el)
    this.editor.normalize()
    this.setCursorPosition(el)
  }

  setCursorPosition(el: Node, collapse: boolean = false) {
    const selObj = window.getSelection()
    if (!selObj) return

    if (selObj.rangeCount > 0) {
      selObj.removeAllRanges()
    }

    const range = document.createRange()
    range.selectNode(el)
    range.collapse(collapse)
    selObj.addRange(range)
  }
}
